/*
 * Copyright (C) 2010, Funcom AS, All Rights Reserved
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  @authors
 *  Svein Gunnar Larsen <sveingunnarlarsen@gmail.com>
 *  Erik Thune Lund <culex.eriklund@gmail.com>
 *  Michel Krohn-Dale <kronicz@gmail.com>
 */

package com.funcom.peeler.tool;

import com.funcom.peeler.WorkBench;
import com.funcom.peeler.action.Change;
import com.funcom.peeler.action.Move;
import com.funcom.peeler.action.MultiChange;
import com.funcom.peeler.action.Resize;
import com.funcom.peeler.peelable.PeelCanvasWindow;
import com.funcom.peeler.peelable.PeelComponent;
import com.funcom.peeler.peelable.PeelContainer;
import com.funcom.peeler.peelable.PeelWorkSpace;
import com.jmex.bui.BComponent;
import com.jmex.bui.util.Rectangle;

import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;

public class ResizeTool extends DraggingTool {

    private double scale;

    public ResizeTool( WorkBench workbench ) {
        super( workbench );
    }

    public void mousePressed( MouseEvent e ) {

        if ( e.getButton() == e.BUTTON3 && change != null ) {
            deInit();
            workbench.undo();
            return;
        }

        PeelComponent component = bui.getHitComponent( e );

        if ( component == null || component instanceof PeelWorkSpace || component instanceof PeelCanvasWindow ) {
            bui.clearSelections();
            return;
        }

        if ( bui.getSelections().size() <= 1 ) bui.clearSelections();

        bui.select( component );

        if ( e.getButton() == e.BUTTON1 ) {
            prev_point = e.getPoint();
            start_point = e.getPoint();
            end_point = e.getPoint();

            Collection<Change> changes = new ArrayList<Change>();
            for ( PeelComponent tmp : bui.getSelections() ) {
                changes.add( new Resize( tmp ) );
                changes.add( new Move( tmp ) );

                if ( tmp instanceof PeelContainer ) {
                    for ( PeelComponent child : ((PeelContainer)tmp).getDescendants() ) {
                        changes.add( new Resize( child ) );
                        changes.add( new Move( child ) );
                    }
                }
            }

            change = new MultiChange( changes );
            change.startChange();
            change.endChange();
        }
    }

    public void mouseReleased( MouseEvent e ) {

        if ( e.getButton() == e.BUTTON1 ) {
            deInit();
        }
    }

    public void mouseDragged( MouseEvent e ) {

        if ( prev_point == null ) return;
        updateDelta( e );

        if ( bui.getPeelRoot().getToolBar().snapEnabled()) {
            snapResize( e );
        }
        else {
            dragResize( e );
        }
    }

    protected void dragResize( final MouseEvent e ) {

        bui.execute( new WorkBench.Task() {

            public void run() {

                if ( change == null ) return;
                change.undoChange();

                for ( PeelComponent component : bui.getSelections() ) {

                    if ( component == null ) return;
                    if ( component instanceof PeelCanvasWindow) return;
                    if ( component instanceof PeelWorkSpace) return;

                    if ( component.isLocked() ) continue;

                    int newX = component.getComponent().getWidth() + start_end_delta.x;
                    int newY = component.getComponent().getHeight() + start_end_delta.y;

                    Point new_size = controlBounds( component, newX, newY );

                    float scale_x = new_size.x * 1.0f / component.getComponent().getWidth();
                    float scale_y = new_size.y * 1.0f / component.getComponent().getHeight();

                    scaleContents( component, scale_x, scale_y );

                    component.getComponent().setSize( new_size.x, new_size.y );
                }
            }
        });
    }

    protected void snapResize( final MouseEvent e ) {

        bui.execute( new WorkBench.Task() {

            public void run() {

                if ( change == null ) return;

                change.undoChange();

                for ( PeelComponent component : bui.getSelections() ) {

                    if ( component == null ) return;
                    if ( component instanceof PeelCanvasWindow ) return;
                    if ( component instanceof PeelWorkSpace ) return;

                    // If locked, do nothing
                    if ( component.isLocked() ) continue;

                    // get the grid step and offset for the grid
                    int grid_step = bui.getPeelRoot().getToolBar().getGridStep();
                    final Point offset = new Point( bui.getWorkSpace().getCanvas().getComponent().getX() % grid_step,
                                                    bui.getWorkSpace().getCanvas().getComponent().getY() % grid_step );

                    // get the old coords and translate the mouse coords to workspace coords
                    Point old_coords = new Point( component.getComponent().getX(), component.getComponent().getY() );
                    for ( PeelComponent tmp = component.getParent() ; !(tmp instanceof PeelWorkSpace) ; tmp = tmp.getParent() ) {
                        old_coords.translate( tmp.getComponent().getX(), tmp.getComponent().getY() );
                    }

                    // get the old coords + how much the mouse has moved - the offset of the grid for native grid coords
                    // then divide by the grid stepping size to find out which grid square it concerns( int division, gives us the integer of the grid square )
                    Point grid_loc = new Point(
                            ( old_coords.x + component.getComponent().getWidth() + start_end_delta.x - offset.x ) / grid_step ,
                            ( old_coords.y + component.getComponent().getHeight() + start_end_delta.y - offset.y ) / grid_step );

                    // multiply by the grid step to find workspace coords for the grid square to snap to
                    grid_loc.x *= grid_step;
                    grid_loc.y *= grid_step;
                    grid_loc.translate( offset.x, offset.y );

                    // translate grid_loc to component local coords
                    grid_loc.translate( -component.getComponent().getX(), -component.getComponent().getY() );
                    for ( PeelContainer tmp = component.getParent() ; !( tmp instanceof PeelWorkSpace ) ; tmp = tmp.getParent() ) {
                        grid_loc.translate( -tmp.getComponent().getX(), -tmp.getComponent().getY() );
                    }

                    // Control the bounds and make sure the component fits in
                    Point new_coords = controlBounds( component, grid_loc.x, grid_loc.y );

                    float scale_x = new_coords.x * 1.0f / component.getComponent().getWidth();
                    float scale_y = new_coords.y * 1.0f / component.getComponent().getHeight();

                    scaleContents( component, scale_x, scale_y );

                    component.getComponent().setSize( new_coords.x, new_coords.y );
                }
            }
        });
    }

    protected Point controlBounds( PeelComponent component, int new_width, int new_height ) {

        if (new_width < 10) {
            new_width = 10;
        }

        if (new_height < 10) {
            new_height = 10;
        }

        Rectangle parent_bounds = component.getComponent().getParent().getBounds();

        new_width = ( component.getComponent().getX() + new_width ) > parent_bounds.width ?
                parent_bounds.width - component.getComponent().getX() :
                new_width;

        new_height = ( component.getComponent().getY()) + new_height > parent_bounds.height ?
                parent_bounds.height - component.getComponent().getY() :
                new_height;

        return new Point( new_width, new_height );
    }

    protected void scaleContents( PeelComponent component, float scale_x, float scale_y ) {

        if ( component instanceof PeelContainer) {

            for ( PeelComponent child : ((PeelContainer)component).getChildren() ) {

                scaleContents( child, scale_x, scale_y );

                BComponent bChild = child.getComponent();
                bChild.setLocation( Math.round( bChild.getX() * scale_x), Math.round(bChild.getY() * scale_y) );
                bChild.setSize( Math.round(bChild.getWidth() * scale_x), Math.round(bChild.getHeight() * scale_y ) );
            }
        }
    }
}
